package com.s2.m.btchat;

import java.io.IOException;
import java.util.Vector;
import javax.bluetooth.*;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.StringItem;

/**
 * This class searches for devices and service
 * @author Samik Saha
 */
public class DeviceDiscovery implements
        CommandListener, Runnable, DiscoveryListener {

    /**
     * UUID of the chat service
     */
    private static final String CHAT_SERVICE_UUID =
            "f631b1e0842211deb89a0002a5d5c51b";
    /**
     * Parent Midlet
     */
    private BTChat parent;
    /**
     * Display of the current midlet
     */
    private Display display;
    /**
     * A Form to display status messages and device list
     */
    private Form form = new Form("Devices");
    /**
     * List of Devices
     */
    private List deviceList = new List("Devices", List.IMPLICIT);
    /**
     * A String to display status
     */
    private StringItem status = new StringItem(null, "");
    /**
     * A Gauge to show the device and service discovery progress
     */
    Gauge discoveryRunningGauge = new Gauge("Discovery in progress",
            false, Gauge.INDEFINITE, Gauge.CONTINUOUS_RUNNING);
    /**
     * Bluetooth device of the current System
     */
    private LocalDevice localDevice = null;
    /**
     * Discovery Agent of the local device
     */
    private DiscoveryAgent discoveryAgent = null;
    /**
     * array of found devices
     */
    private Vector devicesFound = new Vector();
    /**
     * array of found services
     */
    private Vector servicesFound = new Vector();
    /**
     * An index to select a  device from the devicesFound vector
     */
    private int deviceIndex = 0;
    /**
     * the discovery running status
     */
    private boolean discoveryRunning = false;
    /**
     * Status online image
     */
    private Image onlineImg;
    private String listImgDim;//List Image Dimention

    /* Commands */
    private Command cancelCommand = new Command("Cancel", Command.CANCEL, 1);
    private Command searchCommand = new Command("Search", Command.SCREEN, 1);
    private Command chatCommand = new Command("Chat", Command.ITEM, 1);
    private Command viewProfileCommand = new Command("View Profile",
            Command.ITEM, 2);
    private Command backCommand = new Command("Back", Command.BACK, 1);


    /* Constructor*/
    public DeviceDiscovery(BTChat parent) {
        this.parent = parent;
        display = Display.getDisplay(parent);

        discoveryRunningGauge.setLayout(Item.LAYOUT_2 | Item.LAYOUT_CENTER |
                Item.LAYOUT_EXPAND);

        form.addCommand(cancelCommand);
        form.addCommand(searchCommand);
        form.setCommandListener(this);

        deviceList.addCommand(cancelCommand);
        deviceList.addCommand(searchCommand);
        deviceList.addCommand(chatCommand);
        deviceList.addCommand(viewProfileCommand);
        deviceList.setCommandListener(this);

        int h = display.getBestImageHeight(Display.LIST_ELEMENT);
        String dim = (h >= 16) ? "16x16" : "12x12";
        try {
            onlineImg = Image.createImage("/com/s2/m/res/img/status_online" + dim + ".png");
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    private void print(String message) {
        form.append(message + "\n");
    }

    private void addDevice(RemoteDevice d) {
        devicesFound.addElement(d);
        status.setText("Bluetooth Devices in Range: " + devicesFound.size());
    }

    private void addService(ServiceRecord serviceRecord) {
        servicesFound.addElement(serviceRecord);
        deviceList.append(getUsername(serviceRecord), onlineImg);
    }

    private static String getDeviceName(RemoteDevice dev) {
        try {
            return dev.getFriendlyName(true);
        } catch (IOException ex) {
            return dev.getBluetoothAddress();
        }
    }

    public void searchDevices() {
        deviceList.deleteAll();
        form.deleteAll();
        form.append(status);
        form.append(discoveryRunningGauge);
        form.removeCommand(searchCommand);
        devicesFound.removeAllElements();
        servicesFound.removeAllElements();
        deviceIndex = 0;

        Display.getDisplay(parent).setCurrent(form);

        status.setText("Searching for Bluetooth Devices...");
        print("\n");

        Thread t = new Thread(this);
        t.start();
    }

    public void connectAndStartChat(ServiceRecord serviceRecord) {
        String connUrl = serviceRecord.getConnectionURL(
                ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
        try {
            StreamConnection conn = (StreamConnection) Connector.open(connUrl);
            new ServerHandler(conn, new FriendsProfile(serviceRecord));
        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }

    private void showFriendsProfile(ServiceRecord serviceRecord) {
        FriendsProfile profile = new FriendsProfile(serviceRecord);
        profile.addCommand(backCommand);
        profile.addCommand(chatCommand);
        profile.setCommandListener(this);
        display.setCurrent(profile);
    }

    public void commandAction(Command c, Displayable d) {
        if (c == searchCommand) {
            searchDevices();
        } else if (c == cancelCommand) {
            if (discoveryRunning) {
                discoveryAgent.cancelInquiry(this);
            }
            form.addCommand(searchCommand);
            parent.showUI();
        } else if (c == List.SELECT_COMMAND || c == chatCommand) {
            ServiceRecord sr = (ServiceRecord) servicesFound.elementAt(deviceList.getSelectedIndex());
            connectAndStartChat(sr);
        } else if (c == viewProfileCommand) {
            ServiceRecord sr = (ServiceRecord) servicesFound.elementAt(deviceList.getSelectedIndex());
            showFriendsProfile(sr);
        } else if (c == backCommand) {
            display.setCurrent(deviceList);
        }
    }

    public void run() {
        try {
            localDevice = LocalDevice.getLocalDevice();
            discoveryAgent = localDevice.getDiscoveryAgent();
            discoveryRunning = true;
            discoveryAgent.startInquiry(DiscoveryAgent.GIAC, this);
        } catch (BluetoothStateException ex) {
            print("Failed to Retrieve Local Device (" + ex.getMessage() + ")");
            ex.printStackTrace();
        }
    }

    public void deviceDiscovered(RemoteDevice rd, DeviceClass dc) {
        addDevice(rd);
    }

    public void inquiryCompleted(int type) {
        if (type != DiscoveryListener.INQUIRY_COMPLETED) {
            status.setText("Failed to retrieve list of devices");
        } else {
            startServiceSearch();
        }
    }

    public void serviceSearchCompleted(int arg0, int arg1) {
        deviceIndex++;
        startServiceSearch();
    }

    public void servicesDiscovered(int transID, ServiceRecord[] record) {
        if (record != null) {
            for (int i = 0; i < record.length; i++) {
                addService(record[i]);
                break;
            }
        }
    }

    private void startServiceSearch() {
        if (deviceIndex < devicesFound.size()) {
            try {
                RemoteDevice device = (RemoteDevice) devicesFound.elementAt(deviceIndex);
                status.setText("Checking for Chat Services");

                UUID[] uuidList = new UUID[1];
                uuidList[0] = new UUID(CHAT_SERVICE_UUID, false);
                int[] attrList = new int[6];
                attrList[0] = 0x4320;
                attrList[1] = 0x4321;
                attrList[2] = 0x4322;
                attrList[3] = 0x4323;
                attrList[4] = 0x4324;
                attrList[5] = 0x4325;

                discoveryAgent.searchServices(attrList, uuidList, device, this);
            } catch (BluetoothStateException ex) {
                print("Unable to Start Service Search (" + ex.getMessage() + ")");
            }
        } else {
            discoveryRunning = false;
            form.delete(1);
            if (servicesFound.isEmpty()) {
                status.setText("No Chat Service Found");
                form.addCommand(searchCommand);
            } else {
                deviceList.setSelectCommand(List.SELECT_COMMAND);
                Display.getDisplay(parent).setCurrent(deviceList);
            }
        }
    }

    public void showUI() {
        if (servicesFound.isEmpty()) {
            searchDevices();
        } else {
            display.setCurrent(deviceList);
        }
    }

    private String getUsername(ServiceRecord serviceRecord) {
        String username = "";
        DataElement usernameDataElement =
                serviceRecord.getAttributeValue(0x4320);
        if (usernameDataElement != null && usernameDataElement.getDataType() == DataElement.STRING) {
            username = (String) usernameDataElement.getValue();
        }
        if (!(username.trim().equals(""))) {
            return username;
        } else {
            return getDeviceName(serviceRecord.getHostDevice());
        }
    }
}
